/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Drawing.Design;

namespace DaveSexton.DocProject.Sandcastle.BuildComponents
{
	public sealed class SandcastleBuildComponentAttributes
	{
		#region Public Properties
		public ICollection<Attribute> Attributes
		{
			get
			{
				return attributes.AsReadOnly();
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
		public Attribute[] AttributesArray
		{
			get
			{
				return attributes.ToArray();
			}
		}

		public string Description
		{
			get
			{
				return description;
			}
			set
			{
				description = value;
			}
		}

		public string DisplayName
		{
			get
			{
				return displayName;
			}
			set
			{
				displayName = value;
			}
		}

		public bool HasDisplayName
		{
			get
			{
				return hasDisplayName;
			}
		}

		public bool HasDisplayNameOnType
		{
			get
			{
				return hasDisplayNameOnType;
			}
		}

		public bool HasDescription
		{
			get
			{
				return hasDescription;
			}
		}

		public bool HasDescriptionOnType
		{
			get
			{
				return hasDescriptionOnType;
			}
		}

		public bool HasEditor
		{
			get
			{
				return hasEditor;
			}
		}

		public bool HasEditorOnType
		{
			get
			{
				return hasEditorOnType;
			}
		}

		public bool HasTypeConverter
		{
			get
			{
				return hasTypeConverter;
			}
		}

		public bool HasTypeConverterOnType
		{
			get
			{
				return hasTypeConverterOnType;
			}
		}

		public string EditorTypeName
		{
			get
			{
				return editorTypeName;
			}
		}

		public string ConverterTypeName
		{
			get
			{
				return converterTypeName;
			}
		}

		public Type EditorType
		{
			get
			{
				return Type.GetType(editorTypeName);
			}
		}

		public Type ConverterType
		{
			get
			{
				return Type.GetType(converterTypeName);
			}
		}
		#endregion

		#region Private / Protected
		private string description, displayName;
		private string editorTypeName, converterTypeName;
		private bool hasDescription, hasEditor, hasTypeConverter, hasDisplayName;
		private readonly bool hasDisplayNameOnType, hasDescriptionOnType, hasEditorOnType, hasTypeConverterOnType;
		private readonly Type componentType;
		private readonly List<Attribute> attributes;
		private readonly SandcastleBuildComponent component;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SandcastleBuildComponentAttributes" /> class.
		/// </summary>
		public SandcastleBuildComponentAttributes(SandcastleBuildComponent component, string displayName, string description, Type editorType, bool ensurePropertyGridAttributes)
		{
			if (component == null)
				throw new ArgumentNullException("component");

			this.component = component;
			this.displayName = displayName;
			this.description = description;

			attributes = new List<Attribute>(4);

			if (!string.IsNullOrEmpty(displayName))
			{
				attributes.Add(new DisplayNameAttribute(CreateDisplayName(component.Index, displayName)));
				hasDisplayName = true;
			}

			if (!string.IsNullOrEmpty(description))
			{
				attributes.Add(new DescriptionAttribute(description));
				hasDescription = true;
			}

			if (ensurePropertyGridAttributes || editorType != null)
			{
				if (editorType == null)
					editorType = typeof(Editors.SandcastleBuildComponentConfigFileEditor);

				editorTypeName = editorType.FullName;
				attributes.Add(new EditorAttribute(editorType, typeof(UITypeEditor)));
				hasEditor = true;
			}

			if (ensurePropertyGridAttributes)
			{
				Type type = typeof(TypeConverters.SandcastleBuildComponentTypeConverter);
				converterTypeName = type.FullName;
				attributes.Add(new TypeConverterAttribute(type));
				hasTypeConverter = true;
			}
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SandcastleBuildComponentAttributes" /> class.
		/// </summary>
		public SandcastleBuildComponentAttributes(SandcastleBuildComponent component, Type componentType)
		{
			if (component == null)
				throw new ArgumentNullException("component");

			if (componentType == null)
				throw new ArgumentNullException("componentType");

			this.component = component;
			this.componentType = componentType;

			attributes = GetAttributes(componentType.GetCustomAttributes(true));

			for (int i = 0; i < attributes.Count; i++)
			{
				Attribute attribute = attributes[i];

				DisplayNameAttribute displayNameAttribute = attribute as DisplayNameAttribute;

				if (displayNameAttribute != null)
				{
					displayName = displayNameAttribute.DisplayName;
					attributes[i] = new DisplayNameAttribute(CreateDisplayName(component.Index, displayName));
					hasDisplayName = true;
					hasDisplayNameOnType = true;
				}
				else
				{
					DescriptionAttribute descriptionAttribute = attribute as DescriptionAttribute;

					if (descriptionAttribute != null)
					{
						description = descriptionAttribute.Description;
						hasDescription = true;
						hasDescriptionOnType = true;
					}
					else
					{
						EditorAttribute editorAttribute = attribute as EditorAttribute;

						if (editorAttribute != null && string.Equals(typeof(UITypeEditor).AssemblyQualifiedName, editorAttribute.EditorBaseTypeName, StringComparison.Ordinal))
						{
							editorTypeName = editorAttribute.EditorTypeName;
							hasEditor = true;
							hasEditorOnType = true;
						}
						else
						{
							TypeConverterAttribute typeConverterAttribute = attribute as TypeConverterAttribute;

							if (typeConverterAttribute != null)
							{
								converterTypeName = typeConverterAttribute.ConverterTypeName;
								hasTypeConverter = true;
								hasTypeConverterOnType = true;
							}
						}
					}
				}
			}
		}
		#endregion

		#region Methods
		private static string CreateDisplayName(int index, string displayName)
		{
			return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}. {1}", index + 1, displayName);
		}

		/// <remarks>This method is required because reflection always returns an object[] not an Attribute[].</remarks>
		private static List<Attribute> GetAttributes(object[] array)
		{
			if (array == null)
				return null;

			List<Attribute> attributes = new List<Attribute>(array.Length);

			for (int i = 0; i < array.Length; i++)
			{
				Attribute attribute = array[i] as Attribute;

				if (attribute != null)
					attributes.Add(attribute);
			}

			return attributes;
		}

		public void EnsurePropertyGridAttributes()
		{
			if (!hasDescription)
			{
				attributes.Add(new DescriptionAttribute(component.Description));
				hasDescription = true;
			}

			if (!hasDisplayName)
			{
				displayName = GetSandcastleComponentDisplayName() ?? component.Name;
				attributes.Add(new DisplayNameAttribute(CreateDisplayName(component.Index, displayName)));
				hasDisplayName = true;
			}

			if (!hasEditor)
			{
				Type type;

				type = GetSandcastleComponentEditorType() ?? typeof(Editors.SandcastleBuildComponentConfigFileEditor);

				editorTypeName = type.FullName;
				attributes.Add(new EditorAttribute(type, typeof(UITypeEditor)));
				hasEditor = true;
			}

			if (!hasTypeConverter)
			{
				Type type = GetSandcastleComponentTypeConverterType() ?? typeof(TypeConverters.SandcastleBuildComponentTypeConverter);

				converterTypeName = type.FullName;
				attributes.Add(new TypeConverterAttribute(type));
				hasTypeConverter = true;
			}
		}

		private string GetSandcastleComponentDisplayName()
		{
			if (componentType == typeof(Microsoft.Ddue.Tools.ResolveReferenceLinksComponent2)
				|| componentType == typeof(Microsoft.Ddue.Tools.ResolveReferenceLinksComponent))
				return Resources.Text.ResolveReferencesComponentName;
			else if (componentType == typeof(Microsoft.Ddue.Tools.ResolveConceptualLinksComponent))
				return Resources.Text.ResolveConceptualReferencesComponentName;
			else if (componentType == typeof(Microsoft.Ddue.Tools.SharedContentComponent))
				return Resources.Text.SharedContentComponentName;
			else if (componentType == typeof(Microsoft.Ddue.Tools.IfThenComponent))
				return Resources.Text.IfThenComponentName;
			else if (componentType == typeof(Microsoft.Ddue.Tools.ForEachComponent))
				return Resources.Text.ForEachComponentName;
			else if (componentType == typeof(Microsoft.Ddue.Tools.TransformComponent))
				return Resources.Text.TransformComponentName;
			else if (componentType == typeof(Microsoft.Ddue.Tools.SyntaxComponent))
				return Resources.Text.SyntaxComponentName;
			else
				return null;
		}

		private Type GetSandcastleComponentTypeConverterType()
		{
			if (componentType == typeof(Microsoft.Ddue.Tools.ResolveReferenceLinksComponent2)
				|| componentType == typeof(Microsoft.Ddue.Tools.ResolveReferenceLinksComponent)
				|| componentType == typeof(Microsoft.Ddue.Tools.ResolveConceptualLinksComponent))
				return typeof(TypeConverters.BuiltIn.ResolveReferenceLinksComponent2TypeConverter);
			else if (componentType == typeof(Microsoft.Ddue.Tools.SharedContentComponent))
				return typeof(TypeConverters.BuiltIn.SharedContentComponentTypeConverter);
			else if (componentType == typeof(Microsoft.Ddue.Tools.IfThenComponent))
				return typeof(TypeConverters.BuiltIn.IfThenComponentTypeConverter);
			else if (componentType == typeof(Microsoft.Ddue.Tools.ForEachComponent))
				return typeof(TypeConverters.BuiltIn.ForEachComponentTypeConverter);
			else if (componentType == typeof(Microsoft.Ddue.Tools.TransformComponent))
				return typeof(TypeConverters.BuiltIn.TransformComponentTypeConverter);
			else if (componentType == typeof(Microsoft.Ddue.Tools.SyntaxComponent))
				return typeof(TypeConverters.BuiltIn.SyntaxComponentTypeConverter);
			else
				return null;
		}

		private Type GetSandcastleComponentEditorType()
		{
			if (componentType == typeof(Microsoft.Ddue.Tools.ResolveReferenceLinksComponent2)
				|| componentType == typeof(Microsoft.Ddue.Tools.ResolveReferenceLinksComponent))
				return typeof(Editors.BuiltIn.ResolveReferenceLinksComponent2Editor);
			else if (componentType == typeof(Microsoft.Ddue.Tools.ResolveConceptualLinksComponent))
				return typeof(Editors.BuiltIn.ResolveConceptualLinksComponentEditor);
			else if (componentType == typeof(Microsoft.Ddue.Tools.SharedContentComponent))
				return typeof(Editors.BuiltIn.SharedContentComponentEditor);
			else if (componentType == typeof(Microsoft.Ddue.Tools.ForEachComponent))
				return typeof(SandcastleBuildComponentManagerEditor);
			else if (componentType == typeof(Microsoft.Ddue.Tools.TransformComponent))
				return typeof(Editors.BuiltIn.TransformComponentEditor);
			else if (componentType == typeof(Microsoft.Ddue.Tools.SyntaxComponent))
				return typeof(Editors.BuiltIn.SyntaxComponentEditor);
			else
				return null;
		}
		#endregion
	}
}
